#include "lib_pwm.h"

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <malloc.h>
#include <errno.h>

#define LOG_ERROR(...) 															\
do{																												\
	fprintf(stderr, "%s %d [ERROR] ", __FILE__, __LINE__);	\
	fprintf(stderr, __VA_ARGS__);														\
	fprintf(stderr, "\n");																	\
}while(0)

struct _lib_pwm_t
{
	uint8_t chip;					//pwm 通道
	uint32_t per;					//周期时长
	bool mode_is_normal;	//true 高电平持续时间对应于设定的占空比 false ​低电平持续时间对应于设定的占空比
	float duty_cycle;			//占空比
};

#define PWM_CHIP_PATH "/sys/class/pwm/pwmchip"

/***************************************************************
* Name:   write_data_to_file()
* Input : path 文件路径 str 写入数据
* Output: void
* Return: 0 成功 -1 失败
* Author: heweilong
* Revise: V1.0
* Description: 向文件写数据
***************************************************************/
static int write_data_to_file(const char* path, const char* str)
{
	if (access(path, F_OK) != -1)
	{
		int fd;
		
		if ((fd = open(path, O_WRONLY)) < 0)	//打开写入文件失败
		{
			LOG_ERROR("%s %s %s ", strerror(errno), path, str);
			return -1;
		}
		else																	//打开写入文件成功
		{
			if (write(fd, str, sizeof(str)) != sizeof(str))	//写入字符串
			{
				LOG_ERROR("%s %s %s ", strerror(errno), path, str);
				close(fd); //关闭文件
				return -1;
			}
			else
			{
				close(fd); //关闭文件
				return 0;
			}
		}
	}
	else
	{
		LOG_ERROR("%s %s %s ", strerror(errno), path, str);
		return -1;
	}
	
}

/***************************************************************
* Name:   pwm_export()
* Input : chip 通道号
* Output: void
* Return: 0 成功 -1 失败
* Author: heweilong
* Revise: V1.0
* Description: 导出pwm到用户空间
***************************************************************/
static int pwm_export(uint8_t chip)
{
	size_t len = snprintf(NULL, 0, "%s%d/export", PWM_CHIP_PATH, chip) + 1;
	char* path = calloc(len, sizeof(char));
	if (path != NULL)
	{
		snprintf(path, len, "%s%d/export", PWM_CHIP_PATH, chip);
		int rc = write_data_to_file(path, "0");
		free(path);
		return rc;
	}
	else
	{
		return -1;
	}
}

/***************************************************************
* Name:   pwm_unexport()
* Input : chip 通道号
* Output: void
* Return: 0 成功 -1 失败
* Author: heweilong
* Revise: V1.0
* Description: 取消导出pwm到用户空间
***************************************************************/
static int pwm_unexport(uint8_t chip)
{
	size_t len = snprintf(NULL, 0, "%s%d/unexport", PWM_CHIP_PATH, chip) + 1;
	char* path = calloc(len, sizeof(char));
	if (path != NULL)
	{
		snprintf(path, len, "%s%d/unexport", PWM_CHIP_PATH, chip);
		int rc = write_data_to_file(path, "0");
		free(path);
		return rc;
	}
	else
	{
		return -1;
	}
}

/***************************************************************
* Name:   pwm_set_period()
* Input : chip 通道号 per pwm周期
* Output: void
* Return: 0 成功 -1 失败
* Author: heweilong
* Revise: V1.0
* Description: 设置pwm周期
***************************************************************/
static int pwm_set_period(uint8_t chip, uint32_t per)
{
	size_t len = snprintf(NULL, 0, "%s%d/pwm0/period", PWM_CHIP_PATH, chip) + 1;
	char* path = calloc(len, sizeof(char));
	if (path != NULL)
	{
		snprintf(path, len, "%s%d/pwm0/period", PWM_CHIP_PATH, chip);
		char write_str[20];
		snprintf(write_str, sizeof(write_str), "%d", per);
		int rc = write_data_to_file(path, write_str);
		free(path);
		return rc;
	}
	else
	{
		return -1;
	}
}

/***************************************************************
* Name:   pwm_set_duty_cycle()
* Input : chip 通道号 duty_cycle 一个周期中工作时长
* Output: void
* Return: 0 成功 -1 失败
* Author: heweilong
* Revise: V1.0
* Description: 设置占空比
***************************************************************/
static int pwm_set_duty_cycle(uint8_t chip, uint32_t duty_cycle)
{
	size_t len = snprintf(NULL, 0, "%s%d/pwm0/duty_cycle", PWM_CHIP_PATH, chip) + 1;
	char* path = calloc(len, sizeof(char));
	if (path != NULL)
	{
		snprintf(path, len, "%s%d/pwm0/duty_cycle", PWM_CHIP_PATH, chip);
		char write_str[20];
		snprintf(write_str, sizeof(write_str), "%d", duty_cycle);
		int rc = write_data_to_file(path, write_str);
		free(path);
		return rc;
	}
	else
	{
		return -1;
	}
}

/***************************************************************
* Name:   pwm_set_work_mode()
* Input : chip 通道号 is_normal true 高电平持续时间对应于设定的占空比 false ​低电平持续时间对应于设定的占空比
* Output: void
* Return: 0 成功 -1 失败 
* Author: heweilong
* Revise: V1.0
* Description: 设置工作模式
***************************************************************/
static int pwm_set_work_mode(uint8_t chip, bool is_normal)
{
	size_t len = snprintf(NULL, 0, "%s%d/pwm0/polarity", PWM_CHIP_PATH, chip) + 1;
	char* path = calloc(len, sizeof(char));
	if (path != NULL)
	{
		snprintf(path, len, "%s%d/pwm0/polarity", PWM_CHIP_PATH, chip);
		char write_str[20];
		if (is_normal== true)
		{
			snprintf(write_str, sizeof(write_str), "%s", "normal");
		}
		else
		{
			snprintf(write_str, sizeof(write_str), "%s", "inversed");
		}
		int rc = write_data_to_file(path, write_str);
		free(path);
		return rc;
	}
	else
	{
		return -1;
	}
}

/***************************************************************
* Name:   pwm_set_able()
* Input : chip 通道号 is_enable
* Output: void
* Return: 0 成功 -1 失败
* Author: heweilong
* Revise: V1.0
* Description: 设置使能
***************************************************************/
static int pwm_set_able(uint8_t chip, bool is_enable)
{
	size_t len = snprintf(NULL, 0, "%s%d/pwm0/enable", PWM_CHIP_PATH, chip) + 1;
	char* path = calloc(len, sizeof(char));
	if (path != NULL)
	{
		snprintf(path, len, "%s%d/pwm0/enable", PWM_CHIP_PATH, chip);
		char write_str[20];
		snprintf(write_str, sizeof(write_str), "%d", is_enable == true ? 1 : 0);
		int rc = write_data_to_file(path, write_str);
		free(path);
		return rc;
	}
	else
	{
		return -1;
	}
}

/***************************************************************
* Name:   lib_pwm_init()
* Input : param 初始化参数
* Output: void 
* Return: !=NULL 生成的pwm驱动指针
* Author: heweilong
* Revise: V1.0
* Description: 初始化pwm
***************************************************************/
lib_pwm_t* lib_pwm_init(lib_pwm_init_param_t param)
{
	/*判断参数有效性*/
	if (param.per == 0 ||
			param.default_duty_cycle < 0.0f || param.default_duty_cycle > 100.0f)
	{
		LOG_ERROR("init param fail");
		return NULL;
	}
	else
	{
		// 导出PWM设备
		if (pwm_export(param.chip) == -1)
		{
			LOG_ERROR("pwm export fail");
			return NULL;
		}

		// 设置pwm周期 单位为ns
		if (pwm_set_period(param.chip, param.per) == -1)
		{
			LOG_ERROR("pwm set period fail");
			/*取消导出*/
			pwm_unexport(param.chip);
			return NULL;
		}

		// 设置占空比
		uint32_t duty_cycle = param.per * param.default_duty_cycle / 100;
		if (pwm_set_duty_cycle(param.chip, duty_cycle) == -1)
		{
			LOG_ERROR("pwm set duty cycle fail");
			/*取消导出*/
			pwm_unexport(param.chip);
			return NULL;
		}

		// 设置pwm极性
		if (pwm_set_work_mode(param.chip, param.mode_is_normal) == -1)
		{
			LOG_ERROR("pwm set polarity fail");
			/*取消导出*/
			pwm_unexport(param.chip);
			return NULL;
		}

		// 使能pwm
		if (pwm_set_able(param.chip, true) == -1)
		{
			LOG_ERROR("pwm set polarity fail");
			/*取消导出*/
			pwm_unexport(param.chip);
			return NULL;
		}

		// 保存到生成的pwm结构体中返回
		lib_pwm_t *pwm = malloc(sizeof(lib_pwm_t));
		if (pwm != NULL)
		{
			pwm->chip = param.chip;
			pwm->mode_is_normal = param.mode_is_normal;
			pwm->duty_cycle = param.default_duty_cycle;
			pwm->per = param.per;
			return pwm;
		}
		else
		{
			pwm_set_able(param.chip, false);
			pwm_unexport(param.chip);
			return NULL;
		}
	}
}

/***************************************************************
* Name:   lib_pwm_destore()
* Input : dev 生成的pwm驱动指针
* Output: void 
* Return: 0 成功 <0 失败
* Author: heweilong
* Revise: V1.0
* Description: pwm注销
***************************************************************/
int lib_pwm_destore(lib_pwm_t* dev)
{
	if (dev != NULL)
	{
		// 失能pwm
		pwm_set_able(dev->chip, false);

		// 注销PWM
		pwm_unexport(dev->chip);

		//释放空间
		free(dev);

		return 0;
	}
	else
	{
		return -1;
	}
}

/***************************************************************
* Name:   lib_pwm_set_duty()
* Input : dev 生成的pwm驱动指针 duty_cycle 设置的占空比 0~100%
* Output: void 
* Return: 0 成功 -1 失败
* Author: heweilong
* Revise: V1.0
* Description: pwm占空比设置
***************************************************************/
int lib_pwm_set_duty(lib_pwm_t* dev, float duty_cycle)
{
	if (dev == NULL ||
			duty_cycle < 0.0f || duty_cycle > 100.0f)
	{
		return -1;
	}
	else
	{
		// 设置占空比
		uint32_t time = dev->per * duty_cycle / 100;
		int rc = pwm_set_duty_cycle(dev->chip, time);
		if(rc == 0)
		{
			dev->duty_cycle = duty_cycle;
		}
		return rc;
	}
}

/***************************************************************
* Name:   lib_pwm_set_duty()
* Input : dev 生成的pwm驱动指针 per 周期时长ns
* Output: void 
* Return: 0 成功 -1 失败
* Author: heweilong
* Revise: V1.0
* Description: pwm周期设置
***************************************************************/
int lib_pwm_set_per(lib_pwm_t* dev, uint32_t per)
{
	if (dev == NULL)
	{
		return -1;
	}
	else
	{
		// 设置周期
		int rc = pwm_set_period(dev->chip, per);
		if(rc == 0)
		{
			dev->per = per;
		}
		return rc;
	}
}

/***************************************************************
* Name:   lib_pwm_set_mode()
* Input : dev 生成的pwm驱动指针 is_normal rue 高电平持续时间对应于设定的占空比 false ​低电平持续时间对应于设定的占空比
* Output: void 
* Return: 0 成功 -1 失败
* Author: heweilong
* Revise: V1.0
* Description: pwm工作模式
***************************************************************/
int lib_pwm_set_mode(lib_pwm_t* dev, bool is_normal)
{
	if (dev == NULL)
	{
		return -1;
	}
	else
	{
		// 设置工作模式
		int rc = pwm_set_work_mode(dev->chip, is_normal);
		if(rc == 0)
		{
			dev->mode_is_normal = is_normal;
		}
		return rc;
	}
}